import * as fs from "fs";
import * as path from "path";
import * as vscode from "vscode";

type TypeScanFileOption = {
    path: string;
    fileName: string;
    isFile: boolean;
    name: string;
};
type TypeScanFileCallback = (opt: TypeScanFileOption) => void;
type TypeScanFileExp = {
    ignore: RegExp[];
    config?: any;
    rootPath?: string;
};

export const scanFiles = (scanPath: string, fn: TypeScanFileCallback, options?: TypeScanFileExp): void => {
    if(fs.existsSync(scanPath)) {
        const files = fs.readdirSync(scanPath) || [];
        files.map((file) => {
            const fileName = path.resolve(scanPath, file);
            const ignoreFileName = fileName.replace(/\\/g, "/");
            let shouldRead = true;
            if(options?.ignore) {
                let isIgnore = false;
                for(const igRegExt of options.ignore) {
                    if(igRegExt.test(ignoreFileName)) {
                        isIgnore = true;
                        break;
                    }
                }
                shouldRead = !isIgnore;
            }
            if(options?.config && shouldRead) {
                const configIgnore = options?.config?.ignore || [];
                const rootPath = options?.rootPath || "";
                const ignoreRootPath = rootPath.replace(/\\/g, "/");
                for(const ignoreExpReg of configIgnore) {
                    try {
                        
                        if(Object.prototype.toString.call(ignoreExpReg) === "[object RegExp]" && ignoreExpReg instanceof RegExp) {
                            if(ignoreExpReg.test(ignoreFileName)) {
                                shouldRead = false;
                                break;
                            }
                        } else {
                            const resolvePath = ignoreFileName.replace(ignoreRootPath, "").replace(/^\//,"");
                            const compareIgnorePath = ignoreFileName.replace(/^\//,"");
                            if(resolvePath === compareIgnorePath) {
                                shouldRead = false;
                                break;
                            }
                        }
                    } catch( e ) {
                        console.error(e);
                    }
                }
            }
            if(shouldRead) {
                const lstate = fs.lstatSync(fileName);
                if(lstate.isDirectory()) {
                    scanFiles(fileName, fn);
                } else {
                    typeof fn === "function" && fn({
                        path: scanPath,
                        fileName,
                        name: file,
                        isFile: true
                    });
                }
            }
        });
    }
}
export const readFile = (fileName: string): string => {
    if(fs.existsSync(fileName)) {
        const lstate = fs.lstatSync(fileName);
        if(lstate.isFile()) {
            return fs.readFileSync(fileName, "utf8");
        }
    }
    return "";
}

type TypeReadNextLineOption = {
    document: vscode.TextDocument;
    line: number;
    isNextLine?: boolean;
    skipNum?: number;
};
type TypeReadNextLineCallbackOptions = {
    lineText: string;
    lastResult: any;
    returnValue?: any;
    skipNum: number;
};
type TypeReadNextLineCallback = (options: TypeReadNextLineCallbackOptions) => boolean|undefined;

export const readNextLine = (options: TypeReadNextLineOption, fn: TypeReadNextLineCallback):any => {
    const nextLine = options.isNextLine ? options.line + 1 : options.line - 1;
    const isContinue = options.isNextLine ? nextLine <= options.document.lineCount : nextLine >= 0;
    if(isContinue) {
        const lineData = options.document.lineAt(nextLine);
        const lineText = lineData?.text;
        const callbackOption:TypeReadNextLineCallbackOptions = {
            lineText,
            lastResult: null,
            skipNum: options.skipNum || 0
        };
        const callbackResult = fn(callbackOption);
        if(callbackResult) {
            return readNextLine({
                document: options.document,
                line: nextLine,
                isNextLine: options.isNextLine,
                skipNum: callbackOption.skipNum || 0
            }, fn);
        } else {
            return callbackOption.returnValue;
        }
    } else {
        return null;
    }
}

export const getExtensionConfig = () => {
    const rootFolders = vscode.workspace.workspaceFolders || [];
    const rootPath = rootFolders[0]?.uri?.path;
    const configFileName = path.resolve(rootPath, "eui.vscode.json");
    const configText = readFile(configFileName);
    try {
        if(undefined !== configText && null !== configText && (typeof configText === "string" && configText.length > 0)) {
            const configData = JSON.parse(configText);
            if(configData.ignore) {
                for(let i=0;i<configData.ignore.length;i++) {
                    const regTxt = configData.ignore[i];
                    if(/^\//.test(regTxt) && /\/([ig]{0,1})$/.test(regTxt)) {
                        configData.ignore[i] = (new Function(`return ${regTxt};`))();
                    }
                }
            }
            return configData;
        } else {
            return {};
        }
    } catch (e) {
        console.error(e);
        return {};
    }
}

export const getRootPath = ():string => {
    const rootFolders = vscode.workspace.workspaceFolders || [];
    return rootFolders[0]?.uri?.path;
};

export const readConfig = (fileName: string) => {
    const filePath = path.resolve(__dirname, "../");
    const file = path.resolve(filePath, fileName);
    const txt = readFile(file);
    try {
        return JSON.parse(txt);
    } catch(e) {
        console.error(e);
        return null;
    }
}

export const getResolveFileName = (fileName: string, rootPath?: string): string => {
    const myRootPath = rootPath || getRootPath();
    const compareRootPath = myRootPath.replace(/\\/g, "/");
    const compareFileName = fileName.replace(/\\/g, "/");
    return  compareFileName.substr(0, compareRootPath.length) === compareRootPath ? compareFileName.substr(compareRootPath.length) : compareFileName;
};

export const fileExists = (fileName: string): boolean => fs.existsSync(fileName);